package com.hup.utils.swing.customView.colorChooser;

import com.hup.utils.commons.start.LookThemeUtil;
import com.hup.utils.swing.ColorUtil;
import com.hup.utils.swing.GraphicsUtil;
import lombok.extern.log4j.Log4j2;

import java.awt.*;
import java.awt.image.MemoryImageSource;

/**
 * [矩形彩色版]
 * <pre>
 * 色彩规则:下面都是以[255亮度]下的描述
 * '主色':相同亮度下,主色是恒定的;横向分3块-GRB
 *      [255*2个点]-[255*2个点]-[255*2个点]
 * 'Y色值':从上向下递增[0,255];横向分6块-RB-BG-GR,决定色板高度是256
 * 'X色值':决定色板宽度是255*6(注意非256*6)
 *      x方向,变化范围是[0,255],变化方向是交替的[递增或递减]
 *          其中边缘值[0和255]时是两种色共用一列的:如 (0,255,255),(0,255,0),某色值是0时,另一个色值是255,所以宽度是255*6
 *          实际划分成[255,1][0,254][255,1][0,254][255,1][0,254]
 *      y方向,从上向下递增[n,255],n=[0,255]是第一行(y=0)时的色值
 *      分6块-BR-GB-RG
 * '亮度值':范围[0,255],值越高颜色越亮(与idea的相反);
 *      亮度值减少时,同时减少上述三种色值的上限,也会减少 {@link #AbsColorLabel#pix}的使用长度(实际长度仍为初始化时的最大长度)
 * 算法来源:根据idea[圆形色板]推理得出的,暂时没有找到算法理论说明;
 * .[矩形色板]绘制比[圆形色板]简单,纯xy坐标绘制点的颜色
 * .目前没理解到网上说'圆形中互补色的规则'的作用或使用它
 * </pre>
 *
 * @author hugan, wengjs
 * @date 2020/4/12
 */
@Log4j2
public class RectangleColorLabel extends AbsColorLabel {

    private int PIX_WIDTH = 255 * 6;//1530
    private int PIX_HEIGHT = 256;

    /**
     * 正方形彩色版,绘制区域的[边长,边距]
     */
    private int colorWid, paddingLeft, paddingTop;
    private Color colorNotAlpha;

    /**
     * 亮度值:[0,255]->[亮,暗]
     */
    private int brightness;

    public RectangleColorLabel() {
        int size = (int) (296 * LookThemeUtil.getScreenScale());
        Dimension dimension = new Dimension(size, size);
        setPreferredSize(dimension);
    }

    @Override
    protected int[] initPix() {
        return new int[255 * 6 * 256];//固定的最大长度
    }

    @Override
    protected boolean needUpdateImage(Color newer, Color older) {
        return image == null || getBrightness(newer) != getBrightness(older);
    }

    @Override
    public void updateTo(Color color) {
        colorNotAlpha = new Color(color.getRGB());
        super.updateTo(color);
    }

    @Override
    protected Image updateImageByColor() {
        brightness = getBrightness(color);
        if (brightness == 0) {
            //亮度0时,数组长度是1,只有一个(0,0,0)
            PIX_HEIGHT = PIX_WIDTH =  1;
            pix[0] = Color.BLACK.getRGB();//要有透明度,不然颜色是透明的
            return createImage(new MemoryImageSource(PIX_WIDTH, PIX_HEIGHT, pix, 0, PIX_WIDTH));
        }
        PIX_WIDTH = brightness * 6;
        PIX_HEIGHT = brightness + 1;

        //本色板的透明度不随实际透明度改变,否则屏幕截图的色彩和[颜色值输入框]的不一致;其它色板会的变化
        int a = 255;
        int r, g, b;
        int blockI;//当前是'第几块'[0,5]
        int outR, outG, outB;//y=0时的色值
        for (int col = 0; col < PIX_WIDTH; col++) {
            blockI = col / brightness;
            //log.info("col={}, blockI={}", col, blockI);
            switch (blockI) {
                case 0:
                    g = brightness;
                    //col=0时outB=255; col=254时outB=1
                    outB = brightness - col % brightness;//边缘的b[255,1]
                    for (int row = 0; row < PIX_HEIGHT; row++) {
                        r = row;
                        // b从外向内增=[起始值out]+增量
                        // 增量=[总增量255-out]*[当前行比例(row+1)/256]
                        b = (int) (outB + (brightness - outB) * ((row + 1) / (PIX_HEIGHT + 0d)));
                        setPix(row, col, a, r, g, b);
                    }
                    break;
                case 1:
                    g = brightness;
                    //col=0时outB=0; col=254时outB=254
                    outR = col % brightness;//[0,254]
                    for (int row = 0; row < PIX_HEIGHT; row++) {
                        b = row;
                        r = (int) (outR + (brightness - outR) * ((row + 1) / (PIX_HEIGHT + 0d)));
                        setPix(row, col, a, r, g, b);
                    }
                    break;
                case 2:
                    r = brightness;
                    outG = brightness - col % brightness;
                    for (int row = 0; row < PIX_HEIGHT; row++) {
                        b = row;
                        g = (int) (outG + (brightness - outG) * ((row + 1) / (PIX_HEIGHT + 0d)));
                        setPix(row, col, a, r, g, b);
                    }
                    break;
                case 3:
                    r = brightness;
                    outB = col % brightness;
                    for (int row = 0; row < PIX_HEIGHT; row++) {
                        g = row;
                        b = (int) (outB + (brightness - outB) * ((row + 1) / (PIX_HEIGHT + 0d)));
                        setPix(row, col, a, r, g, b);
                    }
                    break;
                case 4:
                    b = brightness;
                    outR = brightness - col % brightness;
                    for (int row = 0; row < PIX_HEIGHT; row++) {
                        g = row;
                        r = (int) (outR + (brightness - outR) * ((row + 1) / (PIX_HEIGHT + 0d)));
                        setPix(row, col, a, r, g, b);
                    }
                    break;
                case 5:
                    b = brightness;
                    //col=0时outG=0; col=254时outG=254
                    outG = col % brightness;
                    for (int row = 0; row < PIX_HEIGHT; row++) {
                        r = row;
                        g = (int) (outG + (brightness - outG) * ((row + 1) / (PIX_HEIGHT + 0d)));
                        setPix(row, col, a, r, g, b);
                    }
                    break;
            }
        }
        return createImage(new MemoryImageSource(PIX_WIDTH, PIX_HEIGHT, pix, 0, PIX_WIDTH));
    }

    private void setPix(int row, int col, int a, int r, int g, int b) {
        int i = row * PIX_WIDTH + col;
        pix[i] = a << 24 | r << 16 | g << 8 | b;
    }

    @Override
    protected boolean doPaintGraphics(Graphics g) {
        //log.info("w={}, h={}", width, height);
        GraphicsUtil.antiAlias((Graphics2D) g);
        //背景填充当前颜色
        int h2 = (int) (height / 2.0);
        g.setColor(color);//有透明度
        g.fillRect(0, 0, width, h2);//透明度
        g.setColor(colorNotAlpha);
        g.fillRect(0, h2 + 5, width, h2);

        //绘制[彩色版]和[外边框],并适配宽高居中绘制
        int padding = 30;//[彩色版]单侧距离[最近的边界]的距离
        colorWid = width < height ? width - padding * 2 : height - padding * 2;//边长
        int outerD = colorWid + 10;//[外边框]边长
        paddingLeft = (width - colorWid) / 2;
        paddingTop = (height - colorWid) / 2;

        g.setColor(getBackground());
        g.fillRect((width - outerD) / 2, (height - outerD) / 2, outerD, outerD);
        g.drawImage(image, paddingLeft, paddingTop, colorWid, colorWid, null);

        //绘制当前色点:空心圆形
        Point pointInImage = curPointInImage();
        if (pointInImage != null) {
            int x = (int) (pointInImage.x / (PIX_WIDTH + 0d) * colorWid) + paddingLeft;
            int y = (int) (pointInImage.y / (PIX_HEIGHT + 0d) * colorWid) + paddingTop;
            log.debug("pointInImage={}, x={}, y={}", pointInImage, x, y);

            int strokeWidth = 2;
            ((Graphics2D) g).setStroke(new BasicStroke(strokeWidth));
            g.setColor(CURSOR_COLOR_C1);
            //半径是4, 缩放比例算为3次方(解决如2*1.25取整后还是2)
            int r = (int) (4 * Math.pow(LookThemeUtil.getScreenScale(), 3));
            int w = r * 2 + 1;//内圈宽度
            g.drawOval(x - r, y - r, w, w);
            g.setColor(CURSOR_COLOR_C2);
            w = w + strokeWidth * 2;//外圈
            g.drawOval(x - r - strokeWidth, y - r - strokeWidth, w, w);
        } else {
            String msg = "找不到[" + color.getRed() + "," + color.getGreen() + "," + color.getBlue() + "]";
            g.setFont(getFont());
            g.setColor(ColorUtil.reverse(color));//反色
            g.drawString(msg, 0, height - 2);
        }
        return true;
    }

    /**
     * 根据当前颜色从色板中找坐标
     * @return 在pix中的坐标
     */
    public Point curPointInImage() {
        log.debug("color={}", color);
        int r = color.getRed();
        int g = color.getGreen();
        int b = color.getBlue();
        int max = getBrightness(color);
        if (max != brightness) {
            log.warn("当前image亮度不对, max={}, brightness={}", max, brightness);//应该不会发生
            return null;
        }

        int row;
        int colSt;//列开始位置
        //'主色',3块:[255个点*2][255个点*2][255个点*2]
        //'X色值',6块*255列:[255,1][0,254][255,1][0,254][255,1][0,254]

        /*
        关于col范围是[256]的问题,是覆盖测试时,发现丢失点了,调值后找回的;
        256:[有两个max时跨到了右边的色块的情况]
         */
        if (g == max) {
            //'X色值':BR
            if (r <= b) {
                row = r;
                colSt = 0;
                for (int col = 0; col < PIX_HEIGHT; col++) {
                    if (getB((PIX_WIDTH * row) + colSt + col) == b)
                        return new Point(colSt + col, row);
                }
            } else {
                row = b;
                colSt = brightness;
                for (int col = 0; col < PIX_HEIGHT; col++) {
                    //log.info("col={}", col);
                    if (getR((PIX_WIDTH * row) + colSt + col) == r)
                        return new Point(colSt + col, row);
                }
            }
        } else if (r == max) {
            //'X色值':GB
            if (b <= g) {
                row = b;
                colSt = brightness * 2;
                for (int col = 0; col < PIX_HEIGHT; col++) {
                    if (getG((PIX_WIDTH * row) + colSt + col) == g)
                        return new Point(colSt + col, row);
                }
            } else {
                row = g;
                colSt = brightness * 3;
                for (int col = 0; col < PIX_HEIGHT; col++) {
                    if (getB((PIX_WIDTH * row) + colSt + col) == b)
                        return new Point(colSt + col, row);
                }
            }
        } else if (b == max) {
            //'Y色值':RG
            if (g <= r) {
                row = g;
                colSt = brightness * 4;
                for (int col = 0; col < PIX_HEIGHT; col++) {
                    if (getR((PIX_WIDTH * row) + colSt + col) == r)
                        return new Point(colSt + col, row);
                }
            } else {
                row = r;
                colSt = brightness * 5;
                for (int col = 0; col < PIX_HEIGHT; col++) {//255列
                    //pixI = row前面的n行 + 本行的列st + 当前列
                    if (getG((PIX_WIDTH * row) + colSt + col) == g)
                        return new Point(colSt + col, row);
                }
            }

        }
        return null;
    }

    private int getR(int pixI) {
        int pixColor = pix[pixI];
        return (pixColor >> 16) & 0xFF;
    }

    private int getG(int pixI) {
        int pixColor = pix[pixI];
        return (pixColor >> 8) & 0xFF;
    }

    private int getB(int pixI) {
        int pixColor = pix[pixI];
        return (pixColor) & 0xFF;
    }

    @Override
    protected Color getMouseColor(boolean isPress, int x, int y) {
        x -= paddingLeft;
        if (x < 0) x = 0;
        else if (x > colorWid) x = colorWid;
        y -= paddingTop;
        if (y < 0) y = 0;
        else if (y > colorWid) y = colorWid;

        int row = (int) ((y + 0d) / colorWid * PIX_HEIGHT);
        int col = (int) ((x + 0d) / colorWid * PIX_WIDTH);
        if (row >= PIX_HEIGHT) row = PIX_HEIGHT - 1;
        if (col >= PIX_WIDTH) col = PIX_WIDTH - 1;
        log.debug("row={}, col={}", row, col);
        int pixColor = this.pix[row * PIX_WIDTH + col];
        int r = (pixColor >> 16) & 0xFF;
        int g = (pixColor >> 8) & 0xFF;
        int b = (pixColor) & 0xFF;
        int withAlpha = color.getAlpha() << 24 | r << 16 | g << 8 | b;
        log.debug("alpha={}, r={}, g={}, b={}", color.getAlpha(), r, g, b);
        return new Color(withAlpha, true);
    }

}
